package info.cs52.bioinfo.hatien;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import prefuse.data.Node;

public class SankoffAlgorithm implements Algorithm {

	private int valueCount = 4;
	private Input input;
	private LinkedList<Node> tracert = new LinkedList<Node>();
	private LinkedList<Node> tracertS = new LinkedList<Node>();

	private LinkedList<Node> labelTracert = new LinkedList<Node>();
	private LinkedList<Node> labelTracertS = new LinkedList<Node>();

	int index;

	public SankoffAlgorithm(Input input) {
		super();
		this.input = input;
		// valueCount = input.getCharacteristics().length;
		init();
		initResult();
	}

	private void init() {
		index = 0;
		init(input.getTree().getRoot());
		fireStepPerformed();
	}

	private void initResult() {
		tracert.clear();
		tracertS.clear();
		labelTracert.clear();
		labelTracertS.clear();
		initS(input.getTreeS().getRoot());
		solve(input.getTreeS().getRoot(), input.getTree().getRoot());
		label(input.getTreeS().getRoot(), "hacon", input.getTree().getRoot());

		fireStepPerformed();
	}

	private void fireStepPerformed() {
		for (AlgorithmListener lis : listeners) {
			lis.stepPerformed(this);
		}
	}

	private void init(Node node) {
		node.setBoolean("active", false);
		if (node.getChildCount() != 0) {
			node.setString("label",null);
		}
		for (int i = 0; i < valueCount; i++) {
			node.setInt("value" + i, (Integer.MAX_VALUE / 2));
		}
		if (node.getString("label") != null) {
			if (node.getChildCount() == 0) {
				for (int i = 0; i < valueCount; i++) {
					if (input.getCharacteristics()[i].compareToIgnoreCase(node
							.getString("label")) == 0) {
						node.setInt("value" + i, 0);
					}
				}
			}
		}
		for (int i = 0; i < node.getChildCount(); i++) {
			init(node.getChild(i));
		}
	}

	private void initS(Node node) {
		node.setBoolean("active", false);
		for (int i = 0; i < valueCount; i++) {
			node.setInt("value" + i, (Integer.MAX_VALUE / 2));
		}
		if (node.getString("label") != null) {
			for (int i = 0; i < valueCount; i++) {
				if (input.getCharacteristics()[i].compareToIgnoreCase(node
						.getString("label")) == 0) {
					node.setInt("value" + i, 0);
				}
			}
		}
		for (int i = 0; i < node.getChildCount(); i++) {
			init(node.getChild(i));
		}
	}

	public int getValueCount() {
		return valueCount;
	}

	@Override
	public boolean next() {
		// XXX implement
		if (index < tracert.size()) {
			if (index > 0) {
				tracert.get(index - 1).setBoolean("active", false);
			}
			tracert.get(index).setBoolean("active", true);
			for (int i = 0; i < valueCount; i++) {
				tracert.get(index).setInt("value" + i,
						tracertS.get(index).getInt("value" + i));
			}

		}
		if ((index >= tracert.size())
				&& (index < (tracert.size() + labelTracert.size()))) {
			int labelIndex = index - tracert.size();
			if (labelIndex > 0) {
				labelTracert.get(labelIndex - 1).setBoolean("active", false);
			} else {
				tracert.get(index - 1).setBoolean("active", false);
			}
			labelTracert.get(labelIndex).setBoolean("active", true);

			labelTracert.get(labelIndex).setString("label",
					labelTracertS.get(labelIndex).getString("label"));

		}
		if (index < (tracert.size() + labelTracert.size())) {
			index++;
		} else {
			labelTracert.get(index - tracert.size() - 1).setBoolean("active",
					false);
			return false;
		}
		fireStepPerformed();
		return true;
	}

	@Override
	public void rewind() {
		init();
	}

	@Override
	public void end() {
		// XXX implement
		boolean dontStop = true;
		while (dontStop){
			dontStop = next();
		}
		fireStepPerformed();
	}

	private List<AlgorithmListener> listeners = new ArrayList<AlgorithmListener>();

	@Override
	public void addAlgorithmListener(AlgorithmListener lis) {
		listeners.add(lis);
	}

	@Override
	public void removeAlgorithmListener(AlgorithmListener lis) {
		listeners.remove(lis);
	}

	public void solve(Node nodeS, Node node) {
		if (nodeS.getString("label") != null) {
			return;
		} else {
			for (int i = 0; i < nodeS.getChildCount(); i++) {
				solve(nodeS.getChild(i), node.getChild(i));
			}
			for (int i = 0; i < valueCount; i++) {
				int value = 0;
				for (int j = 0; j < nodeS.getChildCount(); j++) {
					int min = Integer.MAX_VALUE / 2;
					for (int k = 0; k < valueCount; k++) {
						int temp = input.getScores()[i][k]
								+ nodeS.getChild(j).getInt("value" + k);
						min = min > temp ? temp : min;
					}
					value += min;
				}
				nodeS.setInt("value" + i, value);
			}
			tracert.offer(node);
			tracertS.offer(nodeS);
		}

	}

	public void label(Node nodeS, String label, Node node) {
		if (nodeS.getString("label") != null) {
			return;
		} else {
			int min = Integer.MAX_VALUE / 2;
			
			
			for (int i = 0; i < valueCount; i++) {
				int parentDiff = 0;
				
				if (label != null) {
					for (int j = 0; j < valueCount; j++) {
						if (input.getCharacteristics()[j]
								.compareToIgnoreCase(label) == 0) {
							parentDiff = input.getScores()[i][j];
						}
					}
				}
				
				int value = nodeS.getInt("value" + i) + parentDiff;
				if (min > value) {
					min = value;
					nodeS.setString("label", input.getCharacteristics()[i]);
				}
			}
/*			if (label != null) {
				for (int i = 0; i < valueCount; i++) {
					if (input.getCharacteristics()[i]
							.compareToIgnoreCase(label) == 0) {
						if (nodeS.getInt("value" + i) == min) {
							nodeS.setString("label",
									input.getCharacteristics()[i]);
						}
					}
				}
			}*/

			labelTracert.offer(node);
			labelTracertS.offer(nodeS);

			for (int i = 0; i < nodeS.getChildCount(); i++) {
				label(nodeS.getChild(i), nodeS.getString("label"),
						node.getChild(i));
			}
		}
	}

}
